<?php
// 应用公共文件
use app\model\home\MemberWx;
use app\model\home\VirtualUser;

/**
 * 强大的打印调试函数
 * https://symfony.com/doc/current/components/var_dumper.html
 * @param $var
 * @return array
 */
function d($var)
{
    foreach (func_get_args() as $v) {
        if (is_string($v) || is_numeric($v)) {
            $v = new \ErrorException(
                $v, 1, E_WARNING
            );
        }
        Symfony\Component\VarDumper\VarDumper::dump($v);
    }
    if (1 < func_num_args()) {
        return func_get_args();
    }
    return $var;
}

/**
 * 检测是postman才打印的函数
 * author: xiaogang.zhou@qq.com
 * datetime: 2020/4/23 13:29
 * @param $var
 * @param bool $die
 */
function pdd($var, $die = true)
{
    $check = \think\facade\Request::header('Postman-Token');
    if (empty($check)) {
        return;
    }
    $die && dd($var);
    d($var);
}

/**
 * 创建原生redis对象，非原生的会有一个zscan死循环的bug
 * bug：https://github.com/phpredis/phpredis/issues/1402
 * @return null|Redis
 */
function redis()
{
    static $_redis = null;
    if (!is_null($_redis)) {
        return $_redis;
    }
    $_redis = new \Redis();
    $config = config('cache.stores.redis');
    $_redis->connect($config['host'], $config['port']);
    return $_redis;
}

/**
 * 计算出两个日期之间的月份
 * @param  [type] $start_date [开始日期，如2014-03]
 * @param  [type] $end_date   [结束日期，如2015-12]
 * @param string $explode [年份和月份之间分隔符，此例为 - ]
 * @param boolean $addOne [算取完之后最后是否加一月，用于算取时间戳用]
 * @return [type]             [返回是两个月份之间所有月份字符串]
 * @author Eric
 */
function dateMonths($start_date, $end_date, $explode = '-', $addOne = false)
{
    //判断两个时间是不是需要调换顺序
    $start_int = strtotime($start_date);
    $end_int = strtotime($end_date);
    if ($start_int > $end_int) {
        $tmp = $start_date;
        $start_date = $end_date;
        $end_date = $tmp;
    }


    //结束时间月份+1，如果是13则为新年的一月份
    $start_arr = explode($explode, $start_date);
    $start_year = intval($start_arr[0]);
    $start_month = intval($start_arr[1]);


    $end_arr = explode($explode, $end_date);
    $end_year = intval($end_arr[0]);
    $end_month = intval($end_arr[1]);


    $data = array();
    $data[] = $start_date;


    $tmp_month = $start_month;
    $tmp_year = $start_year;


    //如果起止不相等，一直循环
    while (!(($tmp_month == $end_month) && ($tmp_year == $end_year))) {
        $tmp_month++;
        //超过十二月份，到新年的一月份
        if ($tmp_month > 12) {
            $tmp_month = 1;
            $tmp_year++;
        }
        $data[] = $tmp_year . $explode . str_pad($tmp_month, 2, '0', STR_PAD_LEFT);
    }


    if ($addOne == true) {
        $tmp_month++;
        //超过十二月份，到新年的一月份
        if ($tmp_month > 12) {
            $tmp_month = 1;
            $tmp_year++;
        }
        $data[] = $tmp_year . $explode . str_pad($tmp_month, 2, '0', STR_PAD_LEFT);
    }


    return $data;
}


/**
 * 下载远程图片
 * @param $url
 * @param bool $type true，使用原名 false重命名
 * @return string
 */
function downloadImage($url, $type = false, $ext = '')
{
    $extension = pathinfo($url)['extension'];
    $ext = $extension ? '.' . $extension : '.jpg';
    $filename = $type ? pathinfo($url, PATHINFO_BASENAME) : md5(time() . rand(1, 10000)) . $ext;
    $filePath = app()->getRootPath() . 'public/storage/images/' . $filename;
    $bt = file_get_contents($url);
    if (empty($bt)) {
        return '';
    }
    file_put_contents($filePath, $bt);
    return $filePath;
}

/**
 * @param $number
 * @return string
 */
function formatNumber($number)
{
    if (empty($number) || !is_numeric($number)) return $number;
    $unit = "";
    if ($number > 1000) {
        $leftNumber = floor($number / 1000);
        $rightNumber = round(($number % 1000) / 1000, 2);
        // $rightNumber = bcmul(($number % 10000) / 10000, '1', 2);
        $number = floatval($leftNumber + $rightNumber);
        $unit = "k";
    } else {
        $decimals = $number > 1 ? 2 : 6;
        $number = (float)number_format($number, $decimals, '.', '');
    }
    return (string)$number . $unit;
}

/**
 * @description:根据数据
 * @param {dataArr:需要分组的数据；keyStr:分组依据}
 * @return:
 */
function dataGroup(array $dataArr, string $keyStr): array
{
    $newArr = [];
    foreach ($dataArr as $k => $val) {    //数据根据日期分组
        $newArr[$val[$keyStr]][] = $val;
    }
    return $newArr;
}

/**
 * 生成随机订单号
 *
 * @return void
 */
function makeOrderNo()
{
    $yCode = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
    $orderSn = $yCode[intval(date('Y')) - 2020] . strtoupper(dechex(date('m'))) . date('d') .
        substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
    return $orderSn;
}

function getTimeArr($endTime, $startTime)
{
    //相差
    $diffTime = $endTime - $startTime;
    //几个半小时
    $diffCount = $diffTime / 1800;
    $timeArr[] = $startTime;
//        dd(date('Y-m-d H:i:s',$info['start_time']));
    for ($i = 1; $i < $diffCount; $i++) {
        if ($i < $diffCount) {
            $timeArr[] = $startTime + 1800 * $i;
        }
    }
    $timeArr[] = $endTime;
    foreach ($timeArr as $time) {
        $dateArr[] = date('Y-m-d H:i', $time);
    }
    return $dateArr;
}

function authCode($string, $operation = 'DECODE', $key = '', $expiry = 0)
{
    // 动态密匙长度，相同的明文会生成不同密文就是依靠动态密匙
    $ckey_length = 4;

    // 密匙
    $key = md5($key);

    // 密匙a会参与加解密
    $keya = md5(substr($key, 0, 16));
    // 密匙b会用来做数据完整性验证
    $keyb = md5(substr($key, 16, 16));
    // 密匙c用于变化生成的密文
    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
    // 参与运算的密匙
    $cryptkey = $keya . md5($keya . $keyc);
    $key_length = strlen($cryptkey);
    // 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$keyb(密匙b)，
    //解密时会通过这个密匙验证数据完整性
    // 如果是解码的话，会从第$ckey_length位开始，因为密文前$ckey_length位保存 动态密匙，以保证解密正确
    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
    $string_length = strlen($string);
    $result = '';
    $box = range(0, 255);
    $rndkey = array();
    // 产生密匙簿
    for ($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
    }
    // 用固定的算法，打乱密匙簿，增加随机性，好像很复杂，实际上对并不会增加密文的强度
    for ($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }
    // 核心加解密部分
    for ($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        // 从密匙簿得出密匙进行异或，再转成字符
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }
    if ($operation == 'DECODE') {
        // 验证数据有效性，请看未加密明文的格式
        if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
            return substr($result, 26);
        } else {
            return '';
        }
    } else {
        // 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因
        // 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码
        return $keyc . str_replace('=', '', base64_encode($result));
    }
}

//调用这个函数，将其幻化为数组，然后取出对应值
function object_array($array)
{
    if (is_object($array)) {
        $array = (array)$array;
    }
    if (is_array($array)) {
        foreach ($array as $key => $value) {
            $array[$key] = object_array($value);
        }
    }
    return $array;
}

/**
 * @param $configKey
 * @return array|mixed
 * 获取配置项值
 */
function getConfig($configKey)
{
    $configValue = \app\model\admin\Config::where('config_key', $configKey)->json(['config_value'])->value('config_value');
    if (empty($configValue)) {
        return [];
    } else {
        return json_decode($configValue, true);
    }
}

/**
 * curl模拟http请求GET/POST
 * @param $url
 * @param null $dataay
 */
function http_request($url, $data = null)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    // 以文件流形式返回
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    if (!empty($data)) {
        // POST请求
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    }

    $output = curl_exec($ch);
    curl_close($ch);

    // 返回数组
    return json_decode($output, true);
}

/**
 * @param $member_id
 * @return mixed|string
 * 根据用户id获取openid
 */
function get_openid_by_id($member_id)
{
    return MemberWx::where('member_id', $member_id)->value('openid') ?: '';
}

/**
 * @return mixed
 * 随机获取一个虚拟用户
 */
function get_rand_one_virtual_avatar()
{
    $virtualOne = VirtualUser::field('id,title,avatar,status,create_time')->orderRaw('rand()')->limit(1)->find();
    return $virtualOne['avatar'];
}